#define WEBSOCKECT_C
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h> //使用 malloc, calloc等动态分配内存方法
#include <time.h>   //获取系统时间
#include <errno.h>
#include <pthread.h>
#include <fcntl.h> //非阻塞
#include <sys/un.h>
#include <arpa/inet.h>  //inet_addr()
#include <unistd.h>     //close()
#include <sys/types.h>  //文件IO操作
#include <sys/socket.h> //
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h> //gethostbyname, gethostbyname2, gethostbyname_r, gethostbyname_r2
#include <sys/un.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/ioctl.h> //SIOCSIFADDR
#include <stdbool.h> //引入 bool 类型
#include <stdint.h>  //引入 int8_t ms_u08 ms_s32 ms_u32 等
#include <libmscommon/mscommon.h>
#include <libmscommon/msmd.h>
#include <libmscommon/msstring.h>
#include <libmscommon/msnetwork.h>
#include <libmscommon/msenv.h>
#include <libmscommon/mstime.h>
#include <libmscommon/mshash.h>
#include <libmslog/mslog.h>
#include "msprotocol.h"

#define FLAG "WS"

//==================== 加密方法BASE64 ====================

//base64编/解码用的基础字符集
static const ms_byte ws_base64char[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

typedef struct WEBSOCKECTContext {
	URLContext tcpctt;
	URLProtocol *tcp_ptl;
} WEBSOCKECTContext;

extern URLProtocol ffurl_tcp_protocol;
static void websockect_optinit(URLContext *s)
{
	OPTVAL_INIT_VAILD("listen",s->opt.ws.listen, 1, 0);	
	OPTVAL_INIT("rw_timeout",s->opt.ws.rw_timeout, 0, 1000);	
	//OPTVAL_INIT_DEFAULT("dataType",s->urlctt_opt.ws_opt.dataType, WDT_TXTDATA);
	s->opt.ws.flag_mask=ms_true;
} 

//==================== websocket部分 ====================

/*******************************************************************************
 * 功能: client端使用随机数构建握手用的key
 * 参数: *key: 随机生成的握手key
 * 返回: key的长度
 ******************************************************************************/
static ms_s32 ws_innerapi_buildShakeKey(char* key)
{
    char tempKey[16] = {0};
    msmd_api_randomStr(tempKey, 16);
    return msmd_api_ascii2Base64(ws_base64char,(const ms_u08*)tempKey, 16, (char*)key);
}

/*******************************************************************************
 * 功能: server端在接收client端的key后,构建回应用的key
 * 参数:
 *      acceptKey: 来自客户端的key字符串
 *      acceptKeyLen: 长度
 *      respondKey:  在 acceptKey 之后加上 GUID, 再sha1哈希, 再转成base64得到 respondKey
 * 返回: respondKey的长度(肯定比acceptKey要长)
 * 说明: 无
 ******************************************************************************/
static ms_s32 ws_innerapi_buildRespondShakeKey(char* acceptKey, ms_u32 acceptKeyLen, char* respondKey)
{
    char* clientKey;
    
    ms_u08* sha1Data;
    ms_s32 i, j, sha1DataTempLen, ret;
    const char guid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    ms_u32 guidLen;

    if (acceptKey == NULL){
        return 0;
    }
    guidLen = sizeof(guid);
	ms_malloc_ret(-1, clientKey, (acceptKeyLen + guidLen + 10)*sizeof(char), "clientKey");
    memcpy(clientKey, acceptKey, acceptKeyLen);
    memcpy(&clientKey[acceptKeyLen], guid, guidLen);

	ms_byte *sha1DataTemp=mshash_api_done(clientKey);
	ms_info("sha1DataTemp:%s",sha1DataTemp);
    sha1DataTempLen = strlen((const char*)sha1DataTemp);
	ms_malloc_ret(-1, sha1Data, (sha1DataTempLen / 2 + 1)*sizeof(char), "sha1Data");
    //把hex字符串如"12ABCDEF",转为数值数组如{0x12,0xAB,0xCD,0xEF}
    for (i = j = 0; i < sha1DataTempLen;)
    {
        if (sha1DataTemp[i] > '9')
            sha1Data[j] = (10 + sha1DataTemp[i] - 'A') << 4;
        else
            sha1Data[j] = (sha1DataTemp[i] - '0') << 4;

        i += 1;

        if (sha1DataTemp[i] > '9')
            sha1Data[j] |= (10 + sha1DataTemp[i] - 'A');
        else
            sha1Data[j] |= (sha1DataTemp[i] - '0');

        i += 1;
        j += 1;
    }

    ret = msmd_api_ascii2Base64(ws_base64char,(const ms_u08*)sha1Data, j, (char*)respondKey);
	ms_deMalloc(sha1Data);
    ms_deMalloc(clientKey);
	ms_deMalloc(sha1DataTemp);
    return ret;
}

/*******************************************************************************
 * 功能: client端收到来自服务器回应的key后进行匹配,以验证握手成功
 * 参数:
 *      clientKey: client端请求握手时发给服务器的key
 *      clientKeyLen: 长度
 *      acceptKey: 服务器回应的key
 *      acceptKeyLen: 长度
 * 返回: 0 成功  -1 失败
 * 说明: 无
 ******************************************************************************/
static ms_s32 ws_innerapi_matchShakeKey(char* clientKey, ms_s32 clientKeyLen, char* acceptKey, ms_s32 acceptKeyLen)
{
    ms_s32 retLen;
    char tempKey[256] = {0};

    retLen = ws_innerapi_buildRespondShakeKey(clientKey, clientKeyLen, tempKey);
    if (retLen != acceptKeyLen){
        ms_errRet(-1,"len err, clientKey[%d] != acceptKey[%d]", retLen, acceptKeyLen);
    }else if (strcmp((const char*)tempKey, (const char*)acceptKey) != 0){
        ms_errRet(-1,"strcmp err, clientKey[%s -> %s] != acceptKey[%s]", clientKey, tempKey, acceptKey);
    }
    return 0;
}

/*******************************************************************************
 * 功能: 构建client端连接服务器时的http协议头, 注意websocket是GET形式的
 * 参数:
 *      ip: 要连接的服务器ip字符串
 *      port: 服务器端口
 *      path: 要连接的端口地址
 *      shakeKey: 握手key, 可以由任意的16位字符串打包成base64后得到
 *      package: 存储最后打包好的内容
 * 返回: 无
 * 说明: 无
 ******************************************************************************/
static void ws_innnerapi_buildHttpHead(char* hostname, ms_s32 port, char* path, char* shakeKey, char* package)
{
	ms_sprintfs(package,
		"GET %s HTTP/1.1\r\n"
		"Connection: Upgrade\r\n"
		"Host: %s:%d\r\n"
		"Sec-WebSocket-Key: %s\r\n"
		"Sec-WebSocket-Version: 13\r\n"
		"Upgrade: websocket\r\n\r\n", 
		path, hostname, port, shakeKey);
}

/*******************************************************************************
 * 功能: 构建server端回复client连接请求的http协议
 * 参数:
 *      acceptKey: 来自client的握手key
 *      acceptKeyLen: 长度
 *      package: 存储
 * 返回: 无
 * 说明: 无
 ******************************************************************************/
static void ws_innnerapi_buildHttpRespond(char* acceptKey, ms_u32 acceptKeyLen, char* package)
{
    const char httpDemo[] =
        "HTTP/1.1 101 Switching Protocols\r\n"
        "Upgrade: websocket\r\n"
        "Server: Microsoft-HTTPAPI/2.0\r\n"
        "Connection: Upgrade\r\n"
        "Sec-WebSocket-Accept: %s\r\n"
        "%s\r\n\r\n"; //时间打包待续, 格式如 "Date: Tue, 20 Jun 2017 08:50:41 CST\r\n"
    time_t now;
    struct tm *tm_now;
    char timeStr[256] = {0};
    char respondShakeKey[256] = {0};
    //构建回应的握手key
    ws_innerapi_buildRespondShakeKey(acceptKey, acceptKeyLen, respondShakeKey);
    //构建回应时间字符串
    time(&now);
    tm_now = localtime(&now);
    strftime(timeStr, sizeof(timeStr), "Date: %a, %d %b %Y %T %Z", tm_now);
    //组成回复信息
    sprintf(package, httpDemo, respondShakeKey, timeStr);
}

/*******************************************************************************
 * 名称: ws_innerapi_buildPKT
 * 功能: websocket数据收发阶段的数据打包, 通常client发server的数据都要mask(掩码)处理, 反之server到client却不用
 * 参数:
 *      data: 准备发出的数据
 *      dataLen: 长度
 *      package: 打包后存储地址
 *      packageMaxLen: 存储地址可用长度
 *      mask: 是否使用掩码     1要   0 不要
 *      type: 数据类型, 由打包后第一个字节决定, 这里默认是数据传输, 即0x81
 * 返回: 打包后的长度(会比原数据长2~14个字节不等) <=0 打包失败 
 * 说明: 无
 ******************************************************************************/
static ms_s32 ws_innerapi_buildPKT(
    ms_u08* data,
    ms_u32 dataLen,
    ms_u08* package,
    ms_u32 packageMaxLen,
    ms_bool mask,
    WSDataType type)
{
    //最小长度检查
    if (packageMaxLen < 2){
        return -1;
    }
    ms_u32 i, pkgLen = 0;
    //掩码
    ms_u08 maskKey[4] = {0};
    ms_u32 maskCount = 0;
    //根据包类型设置头字节
	*package++ =(0x80|type);
    pkgLen += 1;
    //掩码位
    if (mask){
        *package = 0x80;
    }
    //半字节记录长度
    if (dataLen < 126){
        *package++ |= (dataLen & 0x7F);
        pkgLen += 1;
    }else if (dataLen < 65536){//2字节记录长度
        if (packageMaxLen < 4){
            return -1;
        }
        *package++ |= 0x7E;
        *package++ = (ms_u08)((dataLen >> 8) & 0xFF);
        *package++ = (ms_u08)((dataLen >> 0) & 0xFF);
        pkgLen += 3;
    }else{//8字节记录长度
        if (packageMaxLen < 10){
            return -1;
        }
        *package++ |= 0x7F;
        *package++ = 0; //数据长度变量是 ms_u32 dataLen, 暂时没有那么多数据
        *package++ = 0;
        *package++ = 0;
        *package++ = 0;
        *package++ = (ms_u08)((dataLen >> 24) & 0xFF); //到这里就够传4GB数据了
        *package++ = (ms_u08)((dataLen >> 16) & 0xFF);
        *package++ = (ms_u08)((dataLen >> 8) & 0xFF);
        *package++ = (ms_u08)((dataLen >> 0) & 0xFF);
        pkgLen += 9;
    }
    //数据使用掩码时,使用异或解码,maskKey[4]依次和数据异或运算,逻辑如下
    if (mask){
        //长度不足
        if (packageMaxLen < pkgLen + dataLen + 4){
            return -1;
        }
        //随机生成掩码
        msmd_api_randomStr((char*)maskKey, sizeof(maskKey));
        *package++ = maskKey[0];
        *package++ = maskKey[1];
        *package++ = maskKey[2];
        *package++ = maskKey[3];
        pkgLen += 4;
        for (i = 0, maskCount = 0; i < dataLen; i++, maskCount++){
            //maskKey[4]循环使用
            if (maskCount == 4){//sizeof(maskKey))
				maskCount = 0;
			} 
            //异或运算后得到数据
            *package++ = maskKey[maskCount] ^ data[i];
        }
        pkgLen += i;
        //断尾
        *package = '\0';
    }
    //数据没使用掩码, 直接复制数据段
    else
    {
        //长度不足
        if (packageMaxLen < pkgLen + dataLen){
			return -1;
		}
        //这种方法,data指针位置相近时拷贝异常
        // memcpy(package, data, dataLen);
        //手动拷贝
        for (i = 0; i < dataLen; i++){
			*package++ = data[i];
		}
        pkgLen += i;
        //断尾
        *package = '\0';
    }
    return pkgLen;
}

/*******************************************************************************
 * 名称: ws_innerapi_parsePKT
 * 功能: websocket数据收发阶段的数据解包,通常client发server的数据都要mask(掩码)处理,反之server到client却不用
 * 参数:
 *      data: 要解包的数据,解包后的数据会覆写到这里
 *      len: 要解包的数据的长度
 *      retDataLen: 解包数据段长度信息
 *      retHeadLen: 解包头部长度信息
 *      retPkgType: 识别包类型
 * 返回:
 *      0: 格式错误,非标准数据包数据
 *      <0: 识别包但不完整(能解析类型、掩码、长度),返回缺少的数据量(负值)
 *      >0: 解包数据成功,返回数据长度,等于retDataLen
 * 说明:
 *      建议recv时先接收14字节然后解包,根据返回缺失长度再recv一次,最后再解包,这样可有效避免连包时只解析到一包的问题
 ******************************************************************************/
static ms_s32 ws_innerapi_parsePKT(
    ms_u08* data,
    ms_u32 len,
    ms_u32* retDataLen,
    ms_u32* retHeadLen,
    WSDataType* retPkgType)
{
    ms_u32 cIn, cOut;
    //包类型
    ms_u08 type;
    //数据段起始位置
    ms_u32 dataOffset = 2;
    //数据段长度
    ms_u32 dataLen = 0;
    //掩码
    ms_u08 maskKey[4] = {0};
    bool mask = false;
    ms_u08 maskCount = 0;
    //数据长度过短
    if (len < 2)
        return 0;
    //解析包类型
    if ((data[0] & 0x80) == 0x80)
    {
        type = data[0] & 0x0F;
        if (type == 0x00)
            *retPkgType = WDT_MINDATA;
        else if (type == 0x01)
            *retPkgType = WDT_TXTDATA;
        else if (type == 0x02)
            *retPkgType = WDT_BINDATA;
        else if (type == 0x08)
            *retPkgType = WDT_DISCONN;
        else if (type == 0x09)
            *retPkgType = WDT_PING;
        else if (type == 0x0A)
            *retPkgType = WDT_PONG;
        else
            return 0;
    }
    else
        return 0;
    //是否掩码,及长度占用字节数
    if ((data[1] & 0x80) == 0x80)
    {
        mask = true;
        maskCount = 4;
    }
    //2字节记录长度
    dataLen = data[1] & 0x7F;
    if (dataLen == 126)
    {
        //数据长度不足以包含长度信息
        if (len < 4)
            return 0;
        //2字节记录长度
        dataLen = data[2];
        dataLen = (dataLen << 8) + data[3];
        //转储长度信息
        *retDataLen = dataLen;
        *retHeadLen = 4 + maskCount;
        //数据长度不足以包含掩码信息
        if (len < (ms_u32)(4 + maskCount))
            return -(ms_s32)(4 + maskCount + dataLen - len);
        //获得掩码
        if (mask)
        {
            maskKey[0] = data[4];
            maskKey[1] = data[5];
            maskKey[2] = data[6];
            maskKey[3] = data[7];
            dataOffset = 8;
        }
        else
            dataOffset = 4;
    }
    //8字节记录长度
    else if (dataLen == 127)
    {
        //数据长度不足以包含长度信息
        if (len < 10)
            return 0;
        //使用8个字节存储长度时,前4位必须为0,装不下那么多数据...
        if (data[2] != 0 || data[3] != 0 || data[4] != 0 || data[5] != 0)
            return 0;
        //8字节记录长度
        dataLen = data[6];
        dataLen = (dataLen << 8) | data[7];
        dataLen = (dataLen << 8) | data[8];
        dataLen = (dataLen << 8) | data[9];
        //转储长度信息
        *retDataLen = dataLen;
        *retHeadLen = 10 + maskCount;
        //数据长度不足以包含掩码信息
        if (len < (ms_u32)(10 + maskCount))
            return -(ms_s32)(10 + maskCount + dataLen - len);
        //获得掩码
        if (mask)
        {
            maskKey[0] = data[10];
            maskKey[1] = data[11];
            maskKey[2] = data[12];
            maskKey[3] = data[13];
            dataOffset = 14;
        }
        else
            dataOffset = 10;
    }
    //半字节记录长度
    else
    {
        //转储长度信息
        *retDataLen = dataLen;
        *retHeadLen = 2 + maskCount;
        //数据长度不足
        if (len < (ms_u32)(2 + maskCount))
            return -(ms_s32)(2 + maskCount + dataLen - len);
        //获得掩码
        if (mask)
        {
            maskKey[0] = data[2];
            maskKey[1] = data[3];
            maskKey[2] = data[4];
            maskKey[3] = data[5];
            dataOffset = 6;
        }
        else
            dataOffset = 2;
    }
    //数据长度不足以包含完整数据段
    if (len < dataLen + dataOffset)
        return -(ms_s32)(dataLen + dataOffset - len);
    //解包数据使用掩码时, 使用异或解码, maskKey[4]依次和数据异或运算, 逻辑如下
    if (mask)
    {
        cIn = dataOffset;
        cOut = 0;
        maskCount = 0;
        for (; cOut < dataLen; cIn++, cOut++, maskCount++)
        {
            //maskKey[4]循环使用
            if (maskCount == 4) //sizeof(maskKey))
                maskCount = 0;
            //异或运算后得到数据
            data[cOut] = maskKey[maskCount] ^ data[cIn];
        }
        //断尾
        data[cOut] = '\0';
    }
    //解包数据没使用掩码, 直接复制数据段
    else
    {
        //这种方法,data指针位置相近时拷贝异常
        // memcpy(data, &data[dataOffset], dataLen);
        //手动拷贝
        cIn = dataOffset;
        cOut = 0;
        for (; cOut < dataLen; cIn++, cOut++)
            data[cOut] = data[cIn];
        //断尾
        data[dataLen] = '\0';
    }
    //有些特殊包数据段长度可能为0,这里为区分格式错误返回,置为1
    if (dataLen == 0)
        dataLen = 1;
    return dataLen;
}

/*******************************************************************************
 * 名称: ws_api_replyClient
 * 功能: 服务器回复客户端的连接请求, 以建立websocket连接
 * 参数:
 *      fd: 连接描述符
 *      psecWebSocketKey: 接收到来自客户端的key
 * 返回: >0 建立websocket连接成功 <=0 建立websocket连接失败
 * 说明: 无
 ******************************************************************************/
ms_s32 ws_api_replyClient(ms_s32 fd, ms_byte* psecWebSocketKey)
{
    ms_byte respondPackage[1024] = {0};
    //获取握手key
    ms_s32 len_key = ms_buflen(psecWebSocketKey);
    if (len_key < 1){
        ms_errRet(-1,"Sec-WebSocket-Key not matched\r\n");
    }
    //创建回复key
    ws_innnerapi_buildHttpRespond(psecWebSocketKey, (ms_u32)len_key, respondPackage);
    return msnet_api_epollSend3(fd, respondPackage, ms_buflen(respondPackage));
}

ms_s32 ws_api_send(ms_s32 fd, ms_byte *buf, ms_s32 len,ms_bool flag_mask,WSDataType dataType)
{
	//非包数据发送
	ms_byte* bufSend =ms_null;
	ms_s32 bufLen = 0;
	if (dataType == WDT_NULL){
		bufSend =buf;
		bufLen = len;
	}else{
		//数据打包 +14 预留类型、掩码、长度保存位
		ms_malloc_ret(-1, bufSend, (len + 14), "bufSend");
		bufLen=ws_innerapi_buildPKT((ms_u08*)buf, len, bufSend, (len + 14), flag_mask, dataType);
	}
	ms_s32 lenSend =0;
	if (bufLen >0){
		lenSend = msnet_api_epollSend3(fd,bufSend,bufLen);
	}
	if(buf!=bufSend){
		ms_deMalloc(bufSend);
	}
	return lenSend;
}

/*******************************************************************************
 * 功能: websocket数据接收和基本解包
 * 参数: 
 *      fd: 连接描述符
 *      buff: 数据接收地址
 *      buffSize: 接收区可用最大长度,至少16字节
 * 返回:
 *      =0 没有收到有效数据(或者收到特殊包,如果是 WDT_DISCONN 则fd已被close)
 *      >0 成功接收并解包数据
 *      <0 非标准数据包数据的长度
 * 说明: 无
 ******************************************************************************/
ms_s32 ws_api_recv(ms_s32 fd, void* buf, ms_s32 len, WSDataType* pdataType)
{
    ms_s32 ret;
    ms_s32 retRecv = 0;     //调用recv的返回
    ms_s32 retDePkg;        //调用解包的返回
    ms_u32 retDataLen = 0; //解包得到的数据段长度
    ms_u32 retHeadLen = 0; //解包得到的包头部长度
    ms_s32 retFinal = 0;    //最终返回
    ms_u32 timeout = 0;    //接收超时计数

    char tmp[16]; //为防止一次接收到多包数据(粘包),先尝试性接收ws头部字节,得知总长度后再接收剩下部分
    WSDataType retPkgType = WDT_NULL; //默认返回包类型
    char* cBuff = (char*)buf; //转换指针类型

    //丢弃数据
    if (!buf || len < 1){
		if(!(msnet_api_epollRecv3(fd, tmp, sizeof(tmp))>0)){
			return -1;
		}
    }else{ //先接收数据头部,头部最大2+4+8=14字节
        if (len < 16){
            ms_error("error, len must be >= 16");
        }else{
			retRecv = msnet_api_epollRecv3(fd, buf, 14);
			if(!(retRecv>0)){
				return -1;
			}
        }
    }
	ms_s32 recvlen = 0;
    if (retRecv > 0){
        //数据解包
        retDePkg = ws_innerapi_parsePKT((ms_u08*)buf, retRecv, &retDataLen, &retHeadLen, &retPkgType);
        //1. 非标准数据包数据,再接收一次(防止丢数据),之后返回"负len"长度值
        //2. buffSize不足以收下这一包数据,当作非标准数据包数据处理,能收多少算多少
        if (retDePkg == 0 || (retDePkg < 0 && retRecv - retDePkg > len)){
            //能收多少算多少
			recvlen = msnet_api_epollRecv3(fd, &cBuff[retRecv], (len - retRecv));
			if(!(recvlen>0)){
				return -1;
			}
			retRecv +=recvlen;
            //对于包过大的问题
            if (retDePkg < 0){
                //1. 发出警告
                ms_error("warnning, pkgLen(%d) > len(%d)", retRecv - retDePkg, len);
                //2. 把这包数据丢弃,以免影响后续包
				recvlen = msnet_api_epollRecv3(fd, tmp, sizeof(tmp));
				if(!(recvlen>0)){
					return -1;
				}
            }
            retFinal = -retRecv;
            //显示数据
            //ms_verbose("ws_innerapi_recv1: len/%d retDePkg/%d retDataLen/%d retHeadLen/%d retPkgType/%d",retRecv, retDePkg, retDataLen, retHeadLen, retPkgType);
			//ms_bufHex("buf", buf, retRecv);
        }
        //正常收包
        else
        {
            //检查是否需要续传
            if (retDePkg < 0)
            {
                //再接收一次(通常情况)
				ret = msnet_api_epollRecv3(fd, &cBuff[retRecv], -retDePkg);
                if (ret > 0){
                    retRecv += ret;
                    retDePkg += ret;
                }else{
					return -1;
				}
                //数据量上百K时需要多次recv,无数据200ms超时,继续接收
                for (timeout = 0; timeout < 200 && retDePkg < 0;)
                {
                    ms_msleep(5);
                    timeout += 5;
					ret = msnet_api_epollRecv3(fd, &cBuff[retRecv], -retDePkg);
                    if (ret > 0){
                        timeout = 0;
                        retRecv += ret;
                        retDePkg += ret;
                    }else{
						return -1;
					}
                }
                //显示数据
                //ms_verbose("ws_innerapi_recv2: len/%d retDePkg/%d retDataLen/%d retHeadLen/%d retPkgType/%d",retRecv, retDePkg, retDataLen, retHeadLen, retPkgType);
				//ms_bufHex("buf", buf, retRecv);
                //二次解包
                retDePkg = ws_innerapi_parsePKT((ms_u08*)buf, retRecv, &retDataLen, &retHeadLen, &retPkgType);
            }
            //显示数据
            //ms_verbose("ws_innerapi_recv3: len/%d retDePkg/%d retDataLen/%d retHeadLen/%d retPkgType/%d\r\n",retRecv, retDePkg, retDataLen, retHeadLen, retPkgType);
            //ms_bufHex("buf", buf, retRecv);
            //一包数据终于完整的接收完了...
            if (retDePkg > 0)
            {
                //收到 PING 包,应自动回复 PONG
                if (retPkgType == WDT_PING){
                    //自动 ping-pong
                    ws_api_send(fd, NULL, 0, ms_false, WDT_PONG);
                    retFinal = 0;
                }
                //收到 PONG 包
                else if (retPkgType == WDT_PONG)
                {
                    retFinal = 0;
                }
                //收到 断连 包
                else if (retPkgType == WDT_DISCONN)
                {
                    retFinal = 0;
                }
                //其它正常数据包
                else
                    retFinal = retDePkg;
            }
            //未曾设想的道路...
            else
                retFinal = -retRecv;
        }
    }
    //返回包类型
    if (pdataType)
        *pdataType = retPkgType;

    return retFinal;
}
void websockect_listen(URLContext *s)
{

}
static int websockect_open(URLContext *s)
{
	if(1==s->is_connected){
		return 0;
	}
	websockect_optinit(s);
	WEBSOCKECTContext * ws_ctt=s->priv_data=(void *)ms_mallocDes(sizeof(WEBSOCKECTContext),"WEBSOCKECTContext",s->url);
	if(NULL==s->priv_data){
		ms_errGoto(fail, "ms_malloc WEBSOCKECTContext faild(%d,%s)",errno,strerror(errno));
	}
	memset(ws_ctt,0,sizeof(WEBSOCKECTContext));	
	
	msptc_api_urlSplit_urlctt( s);
	if(-1==s->url_info.port){
		s->url_info.port=80;
	}
	ms_verbose("open %s,flags:%d", s->url ,s->flags);

	if(ms_true==s->url_info.flag_ipv6){
		ms_sprintfs(ws_ctt->tcpctt.url,"tcp://[%s]:%d",s->url_info.hostname,s->url_info.port);
	}else{
		ms_sprintfs(ws_ctt->tcpctt.url,"tcp://%s:%d",s->url_info.hostname,s->url_info.port);
	}

	ws_ctt->tcp_ptl=&ffurl_tcp_protocol;
	
	if(1==s->opt.ws.listen){
		ws_ctt->tcpctt.opt.tcp.listen=1;
	}
	ws_ctt->tcpctt.ifcIn=s->ifcIn;
	ws_ctt->tcpctt.opt.tcp.rw_timeout=s->opt.ws.rw_timeout;
	ms_debug("tcp_open");
	if(ws_ctt->tcp_ptl->url_open(&ws_ctt->tcpctt)<0){
		ms_error("url:%s,location:%s",s->url,s->location);	
		ms_errGoto(fail, "[%s]Open %s error",s->url,ws_ctt->tcpctt.url);	
	}
	ms_debug("tcp_open11");

	if(1==s->opt.ws.listen){
		websockect_listen(s);
	}else{
		char shakeKey[128] = {0};
		char httpHead[512] = {0};
		//创建握手key
		ms_debug("ws_innerapi_buildShakeKey");
	    ws_innerapi_buildShakeKey(shakeKey);         
	    //创建协议包
	    ms_debug("ws_innnerapi_buildHttpHead");
	    ws_innnerapi_buildHttpHead(s->url_info.hostname, s->url_info.port, s->url_info.path, shakeKey, (char*)httpHead); //组装http请求头
	    ms_debug("url_write");
		int sendlen=ws_ctt->tcp_ptl->url_write(&ws_ctt->tcpctt,httpHead,strlen(httpHead));
		if(sendlen!=strlen(httpHead)){
			ms_errGoto(fail, "Send request faild(%d-%d),error:  %d %s",sendlen,(int)strlen(httpHead), errno, strerror(errno));
		}
	
		char retBuff[512] = {0};
		char* p;
		ms_debug("url_read");
	    int reply_buf_len=ws_ctt->tcp_ptl->url_read(&ws_ctt->tcpctt,retBuff,sizeof(retBuff));
		if (!(reply_buf_len > 0)){
			ms_errGoto(fail, "recv: len %d", reply_buf_len);
		}
		if (ms_strncmp_neq(retBuff, "HTTP", ms_buflen("HTTP"))){
			ms_errGoto(fail, "recv: len %d / unknown context\r\n%s", reply_buf_len, retBuff);
		}
		ms_verbose("recv: len %d -context\r\n%s", reply_buf_len, retBuff);
	 	if ((p = strstr((char*)retBuff, "Sec-WebSocket-Accept: ")) != NULL){//定位到握手字符串
			p += strlen("Sec-WebSocket-Accept: ");
			sscanf((const char*)p, "%s\r\n", p);
			if (ws_innerapi_matchShakeKey(shakeKey, strlen((const char*)shakeKey), p, strlen((const char*)p)) == 0){//比对握手信息
				goto ms_sucess;
			}else{//握手信号不对, 重发协议包
				sendlen=ws_ctt->tcp_ptl->url_write(&ws_ctt->tcpctt,httpHead,strlen(httpHead));
				if(sendlen!=strlen(httpHead)){
					ms_errGoto(fail, "Send request faild(%d-%d),error:  %d %s",sendlen,(int)strlen(httpHead), errno, strerror(errno));
				}
			}
		}else{
			ms_errGoto(fail, "recv: len %d / unknown context\r\n%s", reply_buf_len, retBuff);
		}
	}
ms_sucess:	
	s->is_connected=1;
	return 0;
fail:
	if(NULL!=ws_ctt){
		if(NULL!=ws_ctt->tcp_ptl){
			ws_ctt->tcp_ptl->url_close(&ws_ctt->tcpctt);
		}
		ms_free(ws_ctt);
		ws_ctt=NULL;
	}
	s->is_connected=0;
	return -1;
}

static int websockect_accept(URLContext *s)
{
	if(ms_false==s->is_connected){
		return -1;
	}
	WEBSOCKECTContext * ws_ctt=(WEBSOCKECTContext *)s->priv_data;
	return ws_ctt->tcp_ptl->priv_func.tcp.accept(&ws_ctt->tcpctt);
}

static int websockect_read(URLContext *s ,  unsigned char *buf, int size)
{
	if(ms_false==s->is_connected){
		return -1;
	}
	WEBSOCKECTContext * ws_ctt=(WEBSOCKECTContext *)s->priv_data;
	return ws_api_recv(ws_ctt->tcpctt.fd, buf, size, &s->opt.ws.dataType);
}
static int websockect_write(URLContext *s, unsigned char *buf, int size)
{
	if(ms_false==s->is_connected){
		return -1;
	}
	WEBSOCKECTContext * ws_ctt=(WEBSOCKECTContext *)s->priv_data;
	return ws_api_send(ws_ctt->tcpctt.fd, buf, size,s->opt.ws.flag_mask,s->opt.ws.dataType);
}

static int websockect_get_listen_handle(URLContext *s)
{
	if(ms_false==s->is_connected){
		return -1;
	}
	WEBSOCKECTContext * ws_ctt=(WEBSOCKECTContext *)s->priv_data;
	return ws_ctt->tcp_ptl->priv_func.tcp.get_listen_handle(&ws_ctt->tcpctt);
}

static int websockect_close(URLContext *s)
{
	if(0==s->is_connected){
		return 0;
	}
	WEBSOCKECTContext * ws_ctt=(WEBSOCKECTContext *)s->priv_data;
	if(NULL!=ws_ctt){
		if(NULL!=ws_ctt->tcp_ptl){
			ws_ctt->tcp_ptl->url_close(&ws_ctt->tcpctt);
		}
		ms_freep(ws_ctt);
	}
	s->is_connected=0;
	return 0;
}

URLProtocol ffurl_ws_protocol={
	.nameDes	= msptc_getPtcNameDes(msptc_ws),
    .url_open	= websockect_open,
    .url_read	= websockect_read,
    .url_write	= websockect_write,
    .url_close	= websockect_close,
    .flags		=STREAM_LIVE_VOD,
    .next	=NULL,
};

#undef WEBSOCKECT_C
